package com.easychat.websocket.netty;

import com.easychat.entity.config.AppConfig;
import com.easychat.utils.StringTools;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
//@Slf4j
public class NettyWebSocketStarter implements Runnable {
    // 创建 BossGroup 和 WorkerGroup
    // 1. bossGroup 只处理连接请求
    // 2. 业务处理由 workerGroup 来完成
    private static EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    //DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
    //                "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
    //无参构造器默认的线程数：可用处理器数量*2 -> 16*2
    private static EventLoopGroup workGroup = new NioEventLoopGroup();

    @Resource
    private HandlerWebSocket handlerWebSocket;
    @Resource
    private AppConfig appConfig;

    @PreDestroy
    public void close() {
        bossGroup.shutdownGracefully();
        workGroup.shutdownGracefully();
    }

    @Override
    public void run() {
        try {
            // 创建服务器端的启动对象
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            // 配置参数
            serverBootstrap
                    // 设置线程组
                    .group(bossGroup, workGroup)
                    // 说明服务器端通道的实现类（便于 Netty 做反射处理）
                    .channel(NioServerSocketChannel.class)
                    // 日志
                    // handler()方法用于给 BossGroup 设置业务处理器 **给ServerSocketChannel用的
                    .handler(new LoggingHandler(LogLevel.DEBUG))
                    // childHandler()方法用于给 WorkerGroup 设置业务处理器 **给 SocketChannel 用的
                    .childHandler(new ChannelInitializer<Channel>() {
                        // 创建一个通道初始化对象
                        @Override
                        protected void initChannel(Channel channel) throws Exception {
                            ChannelPipeline pipeline = channel.pipeline();
                            // 向 Pipeline 添加几个业务处理器
                            // 对http协议的支持，使用相应的编码器解码器
                            pipeline.addLast(new HttpServerCodec());
                            // 聚合解码
                            // 参数：消息合并的数据大小，如此代表聚合的消息内容长度不超过64kb
                            pipeline.addLast(new HttpObjectAggregator(64 * 1024));
                            // 心跳
                            // 参数：
                            //long readerIdleTime, 读超时时间 指定为6s
                            // long writerIdleTime, 写超时时间
                            // long allIdleTime, 所有类型的超时时间
                            // TimeUnit unit 单位
                            pipeline.addLast(new IdleStateHandler(6, 0, 0, TimeUnit.SECONDS));
                            //处理心跳超时
                            pipeline.addLast(new HandlerHeartBeat());
                            //将http协议升级为ws协议，对websocket支持
                        /* 以下是典型的使用方法
                            1. 添加 WebSocketServerProtocolHandler
                            pipeline.addLast(new WebSocketServerProtocolHandler(
                                    "/ws", // WebSocket 的路径
                                    null,  // Subprotocols (null 表示没有子协议)
                                    true,  // Allow extensions (true 表示允许扩展)
                                    65536 * 10 // Max frame payload length
                            ));
                            2. 添加自己的 ChannelHandler 来处理 WebSocket 消息
                            pipeline.addLast(new TextWebSocketFrameHandler());
                         */
                            pipeline.addLast(new WebSocketServerProtocolHandler("/ws",null,true,64 * 1024,true,true,10000L));// WebSocket 的路径
                            pipeline.addLast(handlerWebSocket);
                        }
                    });
            // 根据VM Options中配置的信息，决定ws端口号
            Integer wsPort = appConfig.getWsPort();//默认
            String wsPortOption = System.getProperty("ws.port");
            if (!StringTools.isEmpty(wsPortOption)) {
                wsPort = Integer.parseInt(wsPortOption);
            }
            // 绑定端口，启动服务器，生成一个 channelFuture 对象，
            ChannelFuture channelFuture = serverBootstrap.bind(wsPort).sync();
            //log.info("netty启动成功，端口号：{}", appConfig.getWsPort());
            // 对通道关闭进行监听
            channelFuture.channel().closeFuture().sync();
            //处理通道关闭后的操作
        } catch (Exception e) {
            //log.error("启动netty失败", e);
        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();//关闭所有的eventLoop
        }
    }
}
